/*
 * Model.cpp
 *
 *  Created on: 30.10.2012
 *      Author: Martin
 */

#ifdef _WIN32 
#include <windows.h> 
#endif

#include "Model.h"

Model::Model(std::string _filename, std::string _dir)
{
	elementbuffer = 0;
	normalbuffer = 0;
	uvbuffer = 0;
	vertexbuffer = 0;
	tex = 0;

	filename = _filename;
	dir = _dir;

	// create new tex pool for model
	std::string pathbuff = dir + filename + '/';
	textures.setTexPath(pathbuff);

	// no box loaded yet
	isThereBBox = false;

	// bounding box not set
	boundingBox = NULL;
}

Model::~Model()
{
	// cleanup
	glDeleteBuffers(1, &vertexbuffer);
	glDeleteBuffers(1, &uvbuffer);
	glDeleteBuffers(1, &normalbuffer);
	glDeleteBuffers(1, &elementbuffer);

	indexed_vertices.clear();
	indexed_uvs.clear();
	indexed_normals.clear();
}

int Model::loadModel(std::string exfile) // texture pool
{
	std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
	std::vector<glm::vec3> temp_vertices;
	std::vector<glm::vec2> temp_uvs;
	std::vector<glm::vec3> temp_normals;

	std::vector<glm::vec3> out_vertices; // vertices
	std::vector<glm::vec2> out_uvs; // uvs coord
	std::vector<glm::vec3> out_normals; // normals

	std::string matFile = "";
	std::string path;

	if (exfile == "")
		path = dir + filename + "/" + filename + ".obj";
	else
	{
		path = dir + filename + "/" + filename + exfile + ".obj";
	}

#ifdef _DEBUG
	std::cout << "\nSHADERS: Loading model from " << path << std::endl;
#endif
	// path = ex. models/grass01/grass01.obj
	FILE * file = fopen((path).c_str(), "r");
	if (file == NULL)
	{
		std::cerr << "MODEL LOADER: Impossible to open the file " << path << std::endl;
		return 1;
	}

	while (1)
	{
		char lineHeader[128];
		char mtlBuf[128];
		// read the first word of the line
		int res = fscanf(file, "%s", lineHeader);
		if (res == EOF)
			break; // EOF = End Of File. Quit the loop.

		// else : parse lineHeader

		if (strcmp(lineHeader, "v") == 0)
		{
			glm::vec3 vertex;
			fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
			temp_vertices.push_back(vertex);
		}
		else if (strcmp(lineHeader, "vt") == 0)
		{
			glm::vec2 uv;
			fscanf(file, "%f %f\n", &uv.x, &uv.y);
			uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
			temp_uvs.push_back(uv);
		}
		else if (strcmp(lineHeader, "vn") == 0)
		{
			glm::vec3 normal;
			fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
			temp_normals.push_back(normal);
		}
		else if (strcmp(lineHeader, "f") == 0)
		{
			std::string vertex1, vertex2, vertex3;
			unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
			int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n",
					&vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1],
					&uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2],
					&normalIndex[2]);
			if (matches != 9)
			{
				std::cerr << "MODEL LOADER: Cannot load file " << path << std::endl;
				return false;
			}
			vertexIndices.push_back(vertexIndex[0]);
			vertexIndices.push_back(vertexIndex[1]);
			vertexIndices.push_back(vertexIndex[2]);
			uvIndices.push_back(uvIndex[0]);
			uvIndices.push_back(uvIndex[1]);
			uvIndices.push_back(uvIndex[2]);
			normalIndices.push_back(normalIndex[0]);
			normalIndices.push_back(normalIndex[1]);
			normalIndices.push_back(normalIndex[2]);
		}
		else if (strcmp(lineHeader, "mtllib") == 0)
		{ // material file
			fscanf(file, "%s\n", &mtlBuf);
			matFile = mtlBuf;
#ifdef _DEBUG
			std::cout << "SHADERS: Material file: " << matFile << " loading..." << std::endl;
#endif
		}
		else
		{
			// Probably a comment, eat up the rest of the line
			char stupidBuffer[1000];
			fgets(stupidBuffer, 1000, file);
		}

	}

	// For each vertex of each triangle
	for (unsigned int i = 0; i < vertexIndices.size(); i++)
	{

		// Get the indices of its attributes
		unsigned int vertexIndex = vertexIndices[i];
		unsigned int uvIndex = uvIndices[i];
		unsigned int normalIndex = normalIndices[i];

		// Get the attributes thanks to the index
		glm::vec3 vertex = temp_vertices[vertexIndex - 1];
		glm::vec2 uv = temp_uvs[uvIndex - 1];
		glm::vec3 normal = temp_normals[normalIndex - 1];

		// Put the attributes in buffers
		out_vertices.push_back(vertex);
		out_uvs.push_back(uv);
		out_normals.push_back(normal);
	}

	temp_vertices.clear();
	temp_uvs.clear();
	temp_normals.clear();

	// load material file if it exists
	if (matFile.size())
	{
		path = dir + filename + "/" + matFile;

		FILE * file = fopen((path).c_str(), "r");
		if (file == NULL)
		{
			std::cerr << "MODEL LOADER: Impossible to open the file " << path << std::endl;
			return 1;
		}

		while (1)
		{
			char lineHeader[128];
			char texture[200]; //filename of the texture
			// read the first word of the line
			int res = fscanf(file, "%s", lineHeader);
			if (res == EOF)
				break; // EOF = End Of File. Quit the loop.

			if (strcmp(lineHeader, "map_Kd") == 0)
			{ // texture file
				fscanf(file, "%s\n", &texture);
				tex = textures.loadImage(texture);
#ifdef _DEBUG
				std::cout << "SHADERS: Texture file: " << texture << " loading..." << std::endl;
#endif
			}
			else
			{
				// Probably a comment, eat up the rest of the line
				char stupidBuffer[1000];
				fgets(stupidBuffer, 1000, file);
			}
		}
	}

	// create indexing
	indexVBO(out_vertices, out_uvs, out_normals, indices, indexed_vertices,
			indexed_uvs, indexed_normals);

	out_vertices.clear();
	out_uvs.clear();
	out_normals.clear();

	// try to load bounding box
	loadBBox();

	createBufferObject();

	filename = filename + exfile;

	return 0; // ok
}

void Model::loadBBox() //loading bounding box
{
	std::string path = dir + filename + "/" + filename + COL + ".obj";

	FILE * file = fopen((path).c_str(), "r");

	// bounding box found or not?
	if (file != NULL)
	{

		std::vector<glm::vec3> buffBox; // vertices

		while (1)
		{
			char lineHeader[128];
			// read the first word of the line
			int res = fscanf(file, "%s", lineHeader);
			if (res == EOF)
				break; // EOF = End Of File. Quit the loop.

			if (strcmp(lineHeader, "v") == 0)
			{
				glm::vec3 vertex;
				fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
				buffBox.push_back(vertex);
			}
		}

		isThereBBox = true;
#ifdef _DEBUG
		std::cout << "SHADERS: Congratulations, bounding box found!!! Enjoy collisions..."
				<< std::endl;
#endif

		boundingBox = new DataTypes::Cube(buffBox);
	}
}

void Model::createBufferObject()
{

	glGenBuffers(1, &vertexbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3),
			&indexed_vertices[0], GL_STATIC_DRAW);

	glGenBuffers(1, &normalbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
	glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3),
			&indexed_normals[0], GL_STATIC_DRAW);

	glGenBuffers(1, &uvbuffer);
	glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
	glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2),
			&indexed_uvs[0], GL_STATIC_DRAW);

	glGenBuffers(1, &elementbuffer);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short),
			&indices[0], GL_STATIC_DRAW);

}

// draw model on the ogl surface
void Model::draw()
{
	// Bind our texture in Texture Unit 0
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, tex);

	// 1rst attribute buffer : vertices
	glEnableVertexAttribArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
	glVertexAttribPointer(0, // attribute
			3, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
			);

	// 2nd attribute buffer : UVs
	glEnableVertexAttribArray(1);
	glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
	glVertexAttribPointer(1, // attribute
			2, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
			);

	// normals
	glEnableVertexAttribArray(2);
	glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
	glVertexAttribPointer(2, // attribute
			3, // size
			GL_FLOAT, // type
			GL_FALSE, // normalized?
			0, // stride
			(void*) 0 // array buffer offset
			);

	// Index buffer
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);

	// Draw the array !
	glDrawElements(GL_TRIANGLES, // mode
			indices.size(), // count
			GL_UNSIGNED_SHORT, // type
			(void*) 0 // element array buffer offset
			);

	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glDisableVertexAttribArray(2);
}

void Model::drawBoundingBox(glm::vec3 pos, float angle)
{
	// draw bounding box only when there is one
	if (isThereBBox)
	{
		boundingBox->drawCube(pos, angle, glm::vec3(1.0, 0.0, 0.0));
	}
}

std::string Model::getFName()
{
	return filename;
}

bool Model::isIsThereBBox() const
{
	return isThereBBox;
}

void Model::setIsThereBBox(bool isThereBBox)
{
	this->isThereBBox = isThereBBox;
}

